import os

import pandas as pd
import requests
from qiniu import Auth
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

from ModelController import qiniuAPI


def getDataTrain(key):
    access_key, secret_key, bucket_name = qiniuAPI()
    q = Auth(access_key, secret_key)
    bucket_domain = 'qiniuyun.linter.top'
    base_url = f'http://{bucket_domain}/{key}'
    try:
        # 设置token过期时间，生成私有下载URL
        private_url = q.private_download_url(base_url, expires=86400)

        # 发起HTTP GET请求下载文件，并保存到本地
        data_user_dir = os.path.abspath(os.path.join("../modelLinter/static", "../static/LogisticRegressionFile"))
        if not os.path.exists(data_user_dir):
            os.makedirs(data_user_dir)
        local_file_path = os.path.join(data_user_dir, os.path.basename(key))  # 构造本地文件路径
        print(local_file_path)
        with requests.get(private_url, stream=True, timeout=30) as r:  # 使用stream=True以流方式下载
            r.raise_for_status()  # 检查状态码是否为200，如果不是则抛出异常
            with open(local_file_path, 'wb') as f:
                for chunk in r.iter_content(chunk_size=1024):
                    if chunk:
                        f.write(chunk)

    except requests.exceptions.RequestException as req_error:
        # print(f"Network error occurred while downloading file: {req_error}")
        return None

    except Exception as e:
        print(f"Unexpected error occurred: {e}")
        return None

    except Exception as e:
        print(f"Unexpected error occurred: {e}")
        return None


def getDataTest(key):
    access_key, secret_key, bucket_name = qiniuAPI()
    q = Auth(access_key, secret_key)
    bucket_domain = 'qiniuyun.linter.top'
    base_url = f'http://{bucket_domain}/{key}'
    try:
        # 设置token过期时间，生成私有下载URL
        private_url = q.private_download_url(base_url, expires=86400)

        # 发起HTTP GET请求下载文件，并保存到本地
        data_user_dir = os.path.abspath(os.path.join("../modelLinter/static", "../static/LogisticRegressionFile"))
        if not os.path.exists(data_user_dir):
            os.makedirs(data_user_dir)
        local_file_path = os.path.join(data_user_dir, os.path.basename(key))  # 构造本地文件路径
        print(local_file_path)
        with requests.get(private_url, stream=True, timeout=30) as r:  # 使用stream=True以流方式下载
            r.raise_for_status()  # 检查状态码是否为200，如果不是则抛出异常
            with open(local_file_path, 'wb') as f:
                for chunk in r.iter_content(chunk_size=1024):
                    if chunk:
                        f.write(chunk)

    except requests.exceptions.RequestException as req_error:
        # print(f"Network error occurred while downloading file: {req_error}")
        return None

    except Exception as e:
        print(f"Unexpected error occurred: {e}")
        return None

    except Exception as e:
        print(f"Unexpected error occurred: {e}")
        return None


def readDataTrain(key):
    startPath = '../modelLinter/static/LinearFile'
    filrName = '01_train.xlsx'
    filePath = os.path.join(startPath, filrName)
    if os.path.exists(filePath):
        rd = pd.read_excel("../modelLinter/static/LogisticRegressionFile/01_train.xlsx", engine="openpyxl")
        rd = pd.DataFrame(rd)
        # print(rd.head())
        return rd
    else:
        getDataTrain(key)
        rd = pd.read_excel("../modelLinter/static/LogisticRegressionFile/01_train.xlsx", engine="openpyxl")
        rd = pd.DataFrame(rd)
        # print(rd.head())
        return rd


def readDataTest(key):
    startPath = '../modelLinter/static/LinearFile'
    filrName = '02_test.xlsx'
    filePath = os.path.join(startPath, filrName)
    if os.path.exists(filePath):
        rd = pd.read_excel("../modelLinter/static/LogisticRegressionFile/02_test.xlsx", engine="openpyxl")
        rd = pd.DataFrame(rd)
        # print(rd.head())
        return rd
    else:
        getDataTrain(key)
        rd = pd.read_excel("../modelLinter/static/LogisticRegressionFile/02_test.xlsx", engine="openpyxl")
        rd = pd.DataFrame(rd)
        # print(rd.head())
        return rd


class LogisticRegressionPredictor:
    def __init__(self):
        """
        初始化类，传入特征列名列表和目标列名
        :param featureColumns: 特征列名列表
        :param featureColumn: 目标列名
        """
        self.featureColumns = None
        self.featureColumn = None
        self.model = None

    def fit(self, data, test_size_user, random_state_user, featureColumns, featureColumn):
        """
        训练方法，接收一个包含特征和标签的DataFrame作为参数
        :param featureColumn: 目标列名
        :param featureColumns: 特征列名列表
        :param random_state_user: 用户输入参数选择随机程度
        :param test_size_user: 用户输入参数选择测试集大小
        :param data: 用户上传的已经通过预处理的数据集（对于分类问题）
        """
        features = data[featureColumns]
        target = data[featureColumn]

        X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=test_size_user,
                                                            random_state=random_state_user)

        scaler = StandardScaler()
        X_train = scaler.fit_transform(X_train)
        X_test = scaler.transform(X_test)
        self.model = LogisticRegression()  # 可以添加更多参数调整模型
        self.model.fit(X_train, y_train)
        self.featureColumns = featureColumns
        self.featureColumn = featureColumn

    def predict(self, data, featureColumns):
        """
        预测方法，接收一个包含特征的DataFrame进行预测
        :param featureColumns: 特征列名列表
        :param data: 用户上传的已经通过预处理的数据集
        :return: 使用训练好的模型对输入数据进行预测，返回预测的概率或类别（根据任务需求）
        """
        if not self.model:
            raise ValueError("Model has not been trained yet. Please call 'fit' method first.")

        self.featureColumns = featureColumns
        predictions = self.model.predict_proba(data[self.featureColumns])[:, 1]  # 对于二分类问题，返回概率
        # 如果需要返回类别而非概率，则使用以下代码：
        # predictions = self.model.predict(data[self.feature_columns])
        return predictions

    def evaluate(self, data, featureColumn, featureColumns):
        """
        评估方法，计算模型在给定数据上的准确率以及分类报告
        :return: 准确率和分类报告
        """
        if not self.model:
            raise ValueError("Model has not been trained yet. Please call 'fit' method first.")

        y_true = data[featureColumn]
        y_pred_prob = self.predict(data, featureColumns)
        y_pred = (y_pred_prob > 0.5).astype(int)

        accuracy = accuracy_score(y_true, y_pred)
        report = classification_report(y_true, y_pred, zero_division=0)

        return {"accuracy": accuracy, "classification_report": report}
